/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"
#include "Sign_fp.h"

#if CC_Sign  // Conditional expansion of this file

#include "Attest_spt_fp.h"

/*(See part 3 specification)
// sign an externally provided hash using an asymmetric signing key
*/
//  Return Type: TPM_RC
//      TPM_RC_BINDING          The public and private portions of the key are not
//                              properly bound.
//      TPM_RC_KEY              'signHandle' does not reference a signing key;
//      TPM_RC_SCHEME           the scheme is not compatible with sign key type,
//                              or input scheme is not compatible with default
//                              scheme, or the chosen scheme is not a valid
//                              sign scheme
//      TPM_RC_TICKET           'validation' is not a valid ticket
//      TPM_RC_VALUE            the value to sign is larger than allowed for the
//                              type of 'keyHandle'
/*
This command causes the TPM to sign an externally provided hash with the specified symmetric or asymmetric signing key.
NOTE 1 If keyhandle references an unrestricted signing key, a digest can be signed using either this command or an HMAC command.
If keyHandle references a restricted signing key, then validation shall be provided, indicating that the TPM performed 
the hash of the data and validation shall indicate that hashed data did not start with TPM_GENERATED_VALUE.
NOTE 2 If the hashed data did start with TPM_GENERATED_VALUE, then the validation will be a NULL ticket.
The x509sign attribute of keyHandle may not be SET (TPM_RC_ATTRIBUTES).
If the scheme of keyHandle is not TPM_ALG_NULL, then inScheme shall either be the same scheme as keyHandle or TPM_ALG_NULL. 
If the sign attribute is not SET in the key referenced by handle then the TPM shall return TPM_RC_KEY.
If the scheme of keyHandle is TPM_ALG_NULL, the TPM will sign using inScheme; otherwise, it will sign using the scheme of keyHandle.
NOTE 3 When the signing scheme uses a hash algorithm, the algorithm is defined in the qualifying data of the scheme. This 
is the same algorithm that is required to be used in producing digest. 
The size of digest must match that of the hash algorithm in the scheme.
此命令使 TPM 使用指定的对称或非对称签名密钥对外部提供的散列进行签名。
注 1 如果密钥句柄引用不受限制的签名密钥，则可以使用此命令或 HMAC 命令对摘要进行签名。
如果 keyHandle 引用受限签名密钥，则应提供验证，表明 TPM 执行了数据散列，并且验证应表明散列数据不是以 TPM_GENERATED_VALUE 开头。
注 2 如果散列数据确实以 TPM_GENERATED_VALUE 开头，则验证将是 NULL 票证。
keyHandle 的 x509sign 属性可能未设置 (TPM_RC_ATTRIBUTES)。
如果 keyHandle 的方案不是 TPM_ALG_NULL，则 inScheme 应与 keyHandle 或 TPM_ALG_NULL 的方案相同。
如果句柄引用的密钥中未设置符号属性，则 TPM 应返回 TPM_RC_KEY。
如果keyHandle的scheme是TPM_ALG_NULL，TPM会使用inScheme进行签名； 否则，它将使用 keyHandle 方案进行签名。
注3：当签名方案使用散列算法时，该算法在方案的合格数据中定义。 这与生成摘要所需的算法相同。
摘要的大小必须与方案中的散列算法的大小相匹配。

If inScheme is not a valid signing scheme for the type of keyHandle (or TPM_ALG_NULL), then the TPM shall return TPM_RC_SCHEME.
If the scheme of keyHandle is an anonymous scheme, then inScheme shall have the same scheme algorithm as keyHandle and inScheme 
will contain a counter value that will be used in the signing process.
EXAMPLE For ECDAA, inScheme.details.ecdaa.count will contain the count value. If validation is provided, then the hash algorithm 
used in computing the digest is required to be the hash algorithm specified in the scheme of keyHandle (TPM_RC_TICKET).
If the validation parameter is not the Empty Buffer, then it will be checked even if the key referenced by keyHandle is not a 
restricted signing key.
NOTE 4 If keyHandle is both a sign and decrypt key, keyHandle will have a scheme of TPM_ALG_NULL. If validation is provided, then 
it must be a NULL validation ticket or the ticket validation will fail.
如果 inScheme 不是 keyHandle 类型（或 TPM_ALG_NULL）的有效签名方案，则 TPM 应返回 TPM_RC_SCHEME。
如果 keyHandle 的方案是匿名方案，则 inScheme 应具有与 keyHandle 相同的方案算法，并且 inScheme 将包含将在签名过程中使用的计数器值。
示例 对于 ECDAA，inScheme.details.ecdaa.count 将包含计数值。 如果提供验证，则要求计算摘要所用的哈希算法是keyHandle (TPM_RC_TICKET)方案中指定的哈希算法。
如果验证参数不是空缓冲区，则即使 keyHandle 引用的密钥不是受限签名密钥，也会对其进行检查。
注 4 如果 keyHandle 既是签名密钥又是解密密钥，则 keyHandle 将具有 TPM_ALG_NULL 方案。 如果提供验证，则它必须是 NULL 验证票，否则票验证将失败。

*/
TPM_RC
TPM2_Sign(
    Sign_In         *in,            // IN: input parameter list
    Sign_Out        *out            // OUT: output parameter list
    )
{
    TPM_RC                   result;
    TPMT_TK_HASHCHECK        ticket;
    OBJECT                  *signObject = HandleToObject(in->keyHandle);
//
// Input Validation
    if(!IsSigningObject(signObject))
        return TPM_RCS_KEY + RC_Sign_keyHandle;

    // A key that will be used for x.509 signatures can't be used in TPM2_Sign().
    if(IS_ATTRIBUTE(signObject->publicArea.objectAttributes, TPMA_OBJECT, x509sign))
        return TPM_RCS_ATTRIBUTES + RC_Sign_keyHandle;

    // pick a scheme for sign.  If the input sign scheme is not compatible with
    // the default scheme, return an error.
    if(!CryptSelectSignScheme(signObject, &in->inScheme))
        return TPM_RCS_SCHEME + RC_Sign_inScheme;

    // If validation is provided, or the key is restricted, check the ticket
    if(in->validation.digest.t.size != 0
       || IS_ATTRIBUTE(signObject->publicArea.objectAttributes, 
                       TPMA_OBJECT, restricted))
    {
        // Compute and compare ticket
        TicketComputeHashCheck(in->validation.hierarchy,
                               in->inScheme.details.any.hashAlg,
                               &in->digest, &ticket);

        if(!MemoryEqual2B(&in->validation.digest.b, &ticket.digest.b))
            return TPM_RCS_TICKET + RC_Sign_validation;
    }
    else
    // If we don't have a ticket, at least verify that the provided 'digest'
    // is the size of the scheme hashAlg digest.
    // NOTE: this does not guarantee that the 'digest' is actually produced using
    // the indicated hash algorithm, but at least it might be.
    {
        if(in->digest.t.size
           != CryptHashGetDigestSize(in->inScheme.details.any.hashAlg))
            return TPM_RCS_SIZE + RC_Sign_digest;
    }

// Command Output
    // Sign the hash. A TPM_RC_VALUE or TPM_RC_SCHEME
    // error may be returned at this point
    result = CryptSign(signObject, &in->inScheme, &in->digest, &out->signature);

    return result;
}

#endif // CC_Sign